#include "../Logs/superlog.h"

#include <vector>
#include <thread>
#include <chrono>

void bench(const std::string &logger_name, size_t thr_count, size_t msg_count, size_t msg_len)
{
    // 1. 获取日志器
    superlog::Logger::ptr logger = superlog::getLogger(logger_name);
    if(logger.get() == nullptr) return;
    std::cout << "测试日志: " << msg_count << "条, 总大小: " << (msg_count * msg_len) / 1024 << "KB" << std::endl;

    // 2. 组织指定长度的日志消息
    std::string msg(msg_len-1, 'A'); //传入msg_len-1是为了给末尾到时候添加一个换行，以便于观察
    
    // 3. 创建指定数量的线程
    std::vector<std::thread> threads;
    std::vector<double> cost_array(thr_count); //记录每一个线程的耗时
    size_t msg_per_thr = msg_count / thr_count; //每个线程要输出的日志数量 = 总日志数量 / 线程数量
    // 完善：这里可能出现无法整除的情况(最后可以给其中一个线程加上一个取模的数量)

    for(int i = 0; i < thr_count; i++)
    {
        // emplace_back()是构造并插入一个元素，在数组的原有空间基础上构造元素，不需要经历拷贝构造的过程
        threads.emplace_back([&, i](){
            // 4. 线程函数内部开始计时(只计算写日志消耗的时间)
            auto start = std::chrono::high_resolution_clock::now(); //获得当前系统时间

            // 5. 开始循环写日志
            for(int j = 0; j < msg_per_thr; j++)
            {
                logger->fatal("%s", msg.c_str());
            }

            // 6. 线程函数内部结束计时
            auto end = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double> cost = end - start; //cost单位为s，不能直接用auto来代替类型(auto默认单位是ns)
            cost_array[i] = cost.count();
            auto avg = msg_per_thr / cost_array[i];
            std::cout << "线程" << i << ": " << "\t输出数量:" << msg_per_thr << ", 耗时:" << cost_array[i] << "s" << std::endl;
        });
    }
    for(int i = 0; i < thr_count; i++)
    {
        threads[i].join(); //等待线程退出
    }

    // 7. 计算总耗时(在多线程中，每个线程都会耗费时间，但是线程是并发处理的，因此耗时最多的那个就是总时间)
    double max_cost = cost_array[0];
    for(int i = 0; i < thr_count; i++)
    {
        max_cost = max_cost < cost_array[i] ? cost_array[i] : max_cost;
    }
    size_t msg_per_sec = msg_count / max_cost; //每秒输出日志数量
    size_t size_per_sec = (msg_count * msg_len) / (max_cost * 1024); //每秒输出日志大小，单位为KB

    // 8. 进行输出打印
    std::cout << "\t总耗时: " << max_cost << "s" << std::endl;
    std::cout << "\t每秒输出日志数量: " << msg_per_sec << "条" << std::endl;
    std::cout << "\t每秒输出日志大小: " << size_per_sec << "KB" << std::endl;
}

void sync_bench() //同步日志测试
{
    std::unique_ptr<superlog::LoggerBuilder> builder(new superlog::GlobalLoggerBuilder());
    builder->buildLoggerName("sync_logger");
    builder->buildFormatter("%m%n");
    builder->buildLoggerType(superlog::LoggerType::LOGGER_SYNC);
    builder->buildSink<superlog::FileSink>("./logfile/sync.log");
    builder->build();
    bench("sync_logger", 1, 1000000, 100);
    //bench("sync_logger", 3, 1000000, 100);
}

void async_bench() //异步日志测试
{
    std::unique_ptr<superlog::LoggerBuilder> builder(new superlog::GlobalLoggerBuilder());
    builder->buildLoggerName("async_logger");
    builder->buildFormatter("%m%n");
    builder->buildLoggerType(superlog::LoggerType::LOGGER_ASYNC);
    builder->buildEnableUnSafeAsync(); //开始非安全模式--主要是为了将实际落地时间排除在外
    builder->buildSink<superlog::FileSink>("./logfile/async.log");
    builder->build();
    // 为了避免因为等待落地影响时间，所以日志数量需要降低为小于缓冲区大小进行测试
    //bench("async_logger", 1, 1000000, 100);
    bench("async_logger", 3, 1000000, 100);
}

int main()
{
    sync_bench();
    //async_bench();

    return 0;
}